package me.monster.smarttraffic.net;

import com.orhanobut.logger.Logger;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.concurrent.TimeUnit;

import me.monster.smarttraffic.BuildConfig;
import me.monster.smarttraffic.tool.StringUtil;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;

/**
 * @description
 * @author: Created jiangjiwei in 2018/11/1 12:39
 */
public class LogInterceptor implements Interceptor {

    private static final Charset UTF8 = Charset.forName("UTF-8");

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Response response = null;

        HttpUrl url;
        url = original.url();

        Request.Builder builder = original.newBuilder();
        Request request = builder.url(url).build();

        if (BuildConfig.DEBUG) {
            printRequest(chain, request);
        }

        long startNs = System.nanoTime();

        response = chain.proceed(request);

        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
        ResponseBody responseBody = response.body();
        BufferedSource source = responseBody.source();
        source.request(Long.MAX_VALUE);
        Buffer buffer = source.buffer();

        if (BuildConfig.DEBUG) {
            if (printResponse(response, tookMs, responseBody, buffer)) {
                return response;
            }
        }
        return response;
    }

    /**
     * 打印 Response 的具体内容：
     * 1. 具体的返回内容；
     * 2. 返回状态码；
     *
     * @param response     Response
     * @param tookMs       请求时长
     * @param responseBody 返回的具体内容
     * @param buffer       buffer
     * @return response 不为空时返回
     */
    private boolean printResponse(Response response, long tookMs, ResponseBody responseBody, Buffer buffer) {
        StringBuilder responseSb = new StringBuilder();
        long contentLength = responseBody.contentLength();
        responseSb.append("返回<-- ").append(response.code()).append(' ').append(response.message())
                .append(' ').append(response.request().url()).append(" (").append(tookMs).append("ms").append(')')
                .append("\n");

        Headers headers = response.headers();
        for (int i = 0, count = headers.size(); i < count; i++) {
            responseSb.append(headers.name(i)).append(": ").append(headers.value(i)).append("\n");
        }
        Charset charset = UTF8;
        MediaType contentType = responseBody.contentType();
        if (contentType != null) {
            try {
                charset = contentType.charset(UTF8);
            } catch (UnsupportedCharsetException e) {
                Logger.i("Couldn't decode the response body; charset is likely malformed.");
                Logger.i("<-- END HTTP");
                return true;
            }
        }
        if (!StringUtil.isPlaintext(buffer)) {
            Logger.i("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
            return true;
        }

        if (contentLength != 0) {
            Logger.i("返回数据: " + buffer.clone().readString(charset));
        } else {
            responseSb.append("返回信息为空：").append(contentLength).append('\n');
        }

        responseSb.append("<-- END HTTP (").append(buffer.size()).append("-byte body)");
        Logger.i(responseSb.toString());
        return false;
    }

    /**
     * 打印请求包含内容：
     * 1. Request Method
     * 2. Request Body
     *
     * @param chain   连接
     * @param request Request
     * @throws IOException
     */
    private void printRequest(Chain chain, Request request) throws IOException {
        RequestBody requestBody = request.body();
        boolean hasRequestBody = requestBody != null;

        StringBuilder requestSb = new StringBuilder();
        Connection connection = chain.connection();
        Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
        requestSb.append("请求-->").append(' ').append(request.method()).append(' ').append(request.url()).append(' ')
                .append(protocol).append("\n");

        if (hasRequestBody) {
            if (requestBody.contentType() != null) {
                requestSb.append("Content-Type: ").append(requestBody.contentType()).append("\n");
            }
            if (requestBody.contentLength() != -1) {
                requestSb.append("Content-Length: ").append(requestBody.contentLength()).append("-byte body").append
                        ("\n");
            }
        }

        Headers requestHeaders = request.headers();
        for (int i = 0, count = requestHeaders.size(); i < count; i++) {
            String name = requestHeaders.name(i);
            if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                requestSb.append(name).append(": ").append(requestHeaders.value(i));
            }
            requestSb.append("\n");
        }

        if (hasRequestBody) {
            Buffer buffer = new Buffer();
            requestBody.writeTo(buffer);

            Charset charset = UTF8;
            MediaType contentType = requestBody.contentType();
            if (contentType != null) {
                if ("multipart".equals(contentType.type())) {
                    requestSb.append("request body is a multipart ");
                } else {
                    charset = contentType.charset(UTF8);
                }
            }
            long requestBodyLength = requestBody.contentLength();
            if (requestBodyLength / 1024 > 1024) {
                requestSb.append("--> END ").append(request.method()).append(" (binary ")
                        .append(requestBody.contentLength()).append("-byte body omitted)");
            } else {
                requestSb.append(buffer.readString(charset)).append("\n");
                requestSb.append("--> END ").append(request.method());
            }
        }
        Logger.i(requestSb.toString());
    }
}
